﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Windows.Forms;
using ResourceReportBuilder.Common;

namespace ResourceReportBuilder
{
    public partial class GeneratorForm : Form
    {
        private BackgroundWorker _worker;
        private delegate void LogDelegate(string message, Color textColor, bool beginNewLine);

        public GeneratorForm()
        {
            InitializeComponent();
        }

        public void GenerateReport(string projectPath, string ns, string[] substituteFileList, WebResourceCollection resourceList)
        {
            this.ControlBox = false;
            this.Show();
            _worker = new BackgroundWorker();
            _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
            _worker.RunWorkerAsync(new WorkerArgs() { projectPath = projectPath, ns = ns, resourceList = resourceList, substituteFileList = substituteFileList });
            _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
        }

        void _worker_DoWork(object sender, DoWorkEventArgs e)
        {
            WorkerArgs args = (WorkerArgs)e.Argument;
            GenerateReportInternal(args);
            e.Result = args;
        }

        void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.Hide();
            WorkerArgs args = (WorkerArgs)e.Result;
            OutputForm reportFrm = new OutputForm(args.reportPath);
            reportFrm.ShowDialog();

            //reportFrm.ReportLoaded += new EventHandler(reportFrm_ReportLoaded);
        }

        private void GenerateReportInternal(WorkerArgs e)
        {
            try
            {
                string outputFolder = WriteOutputFiles();
                e.reportPath = WriteTemplateHtml(outputFolder, e.projectPath, e.ns, e.substituteFileList, e.resourceList);
            }
            catch (Exception ex)
            {
                Log(string.Format("\r\nERROR: {0}", ex.Message), Color.Red, true);
                this.ControlBox = true;
            }
        }

        void reportFrm_ReportLoaded(object sender, EventArgs e)
        {
        }

        private void Log(string message, bool beginNewLine = false)
        {
            Log(message, Color.Black, beginNewLine);
        }

        private void Log(string message, Color textColor, bool beginNewLine = false)
        {
            if (logBox.InvokeRequired)
            {
                logBox.Invoke(new LogDelegate(Log), message, textColor, beginNewLine);
            }
            else
            {
                string prefix = string.IsNullOrWhiteSpace(logBox.Text) ? "" : (beginNewLine ? "\r\n" : "");
                int start = logBox.Text.Length;
                logBox.SuspendLayout();
                logBox.AppendText(string.Format("{0}{1}", prefix, message));
                logBox.ScrollToCaret();
                logBox.Select(start, logBox.Text.Length - start);
                logBox.SelectionColor = textColor;
                logBox.DeselectAll();
                logBox.ResumeLayout();
                Application.DoEvents();
            }
        }

        private string WriteOutputFiles()
        {
            Log("Preparing report frame...", true);
            string outputDir = "Generated";
            if (Directory.Exists(outputDir))
            {
                try
                {
                    Directory.Delete(outputDir, true);
                }
                catch { }
            }
            Directory.CreateDirectory(outputDir);

            ResManager.WriteToDisk("ResourceReportBuilder.Resources.SyntaxHighlighter.scripts.shAutoloader.js", Path.Combine(outputDir, "scripts"));
            ResManager.WriteToDisk("ResourceReportBuilder.Resources.SyntaxHighlighter.scripts.shBrushCSharp.js", Path.Combine(outputDir, "scripts"));
            ResManager.WriteToDisk("ResourceReportBuilder.Resources.SyntaxHighlighter.scripts.shBrushCss.js", Path.Combine(outputDir, "scripts"));
            ResManager.WriteToDisk("ResourceReportBuilder.Resources.SyntaxHighlighter.scripts.shBrushJScript.js", Path.Combine(outputDir, "scripts"));
            ResManager.WriteToDisk("ResourceReportBuilder.Resources.SyntaxHighlighter.scripts.shBrushPlain.js", Path.Combine(outputDir, "scripts"));
            ResManager.WriteToDisk("ResourceReportBuilder.Resources.SyntaxHighlighter.scripts.shCore.js", Path.Combine(outputDir, "scripts"));
            ResManager.WriteToDisk("ResourceReportBuilder.Resources.SyntaxHighlighter.scripts.shLegacy.js", Path.Combine(outputDir, "scripts"));
            ResManager.WriteToDisk("ResourceReportBuilder.Resources.SyntaxHighlighter.styles.shCoreDefault.css", Path.Combine(outputDir, "styles"));

            //jquery
            ResManager.WriteToDisk("ResourceReportBuilder.Resources.JQuery.jquery-1.5.1.min.js", Path.Combine(outputDir, "jquery", "jquery-1.5.1.min.js"));

            Log("Done.");
            return outputDir;
        }

        private string WriteTemplateHtml(string outputDir, string projectPath, string ns, string[] substituteFileList, WebResourceCollection resourceList)
        {
            Log("Preparing output html...", true);
            string path = Path.Combine(outputDir, ResManager.GetResourceFileName("ResourceReportBuilder.Resources.resultTemplate.html"));
            string templateHtml = ResManager.GetStringResource("ResourceReportBuilder.Resources.resultTemplate.html");

            string[] assemblyWebReferences = GetAssemblyWebReferences(Path.GetDirectoryName(projectPath), ns, resourceList);
            string[] assemblyWebReferenceCalls = GetAssemblyWebReferenceCalls(Path.GetDirectoryName(projectPath), ns, resourceList);
            templateHtml = templateHtml.Replace("{AssemblyWebResources}", string.Join("\r\n", assemblyWebReferences));
            templateHtml = templateHtml.Replace("{WebResourceCalls}", string.Join("\r\n", assemblyWebReferenceCalls));
            templateHtml = templateHtml.Replace("{FileSubstitutions}", SubstituteInFiles(projectPath, ns, substituteFileList, resourceList));
            string headers = string.Empty;
            for (int idx = 0; idx < substituteFileList.Length; idx++)
            {
                headers += string.Format("<li><a class='header' href='#file_{0}'>{1}</a></li>\r\n", idx, substituteFileList[idx]);
            }
            templateHtml = templateHtml.Replace("{TabsHeaders}", headers);
            using (StreamWriter sw = new StreamWriter(path))
            {
                sw.Write(templateHtml);
            }

            return Path.GetFullPath(path);
        }

        private string SubstituteInFiles(string projectPath, string ns, string[] substituteFileList, WebResourceCollection resourceList)
        {
            Log("   - Substituting disk references with web references in selected files...", true);
            if (substituteFileList.Length == 0)
            {
                return string.Empty;
            }

            StringBuilder sb = new StringBuilder();
            string fileContents = string.Empty;
            string syntaxBrush = string.Empty;
            int idx = 0;
            foreach (string fileName in substituteFileList)
            {
                Log(string.Format("      - Searcing in '{0}'...", Path.GetFileName(fileName)), true);
                switch (Path.GetExtension(fileName))
                {
                    case ".css":
                        syntaxBrush = "css";
                        break;
                    case ".js":
                        syntaxBrush = "js";
                        break;
                    default:
                        syntaxBrush = "plain";
                        break;
                }
                fileContents = File.ReadAllText(fileName);
                sb.AppendFormat("<div id='file_{2}' class='content'><h4>{0}</h4><table cellpadding='2' cellspacing='2' border='1' width='100%'><tr><td><script type=\"syntaxhighlighter\" class='brush: {1};toolbar:false;'><![CDATA[\r\n", fileName, syntaxBrush, idx);
                //sb.AppendFormat("<h4>{0}</h4><table cellpadding='2' cellspacing='2' border='1' width='100%'><tr><td><pre class='brush: {1};toolbar:false;'>\r\n", fileName, syntaxBrush);

                Regex regex = null;
                foreach (WebResource res in resourceList)
                {
                    Log(string.Format("         - Searcing for resource '{0}'...", Path.GetFileName(res.Path)), true);
                    switch (syntaxBrush)
                    {
                        case "css":
                            regex = new Regex(string.Format(@"url.*\((.*{0}.*)\)\s*;?", Regex.Escape(Path.GetFileName(res.Path)), RegexOptions.IgnoreCase));
                            foreach (Match match in regex.Matches(fileContents))
                            {
                                fileContents = fileContents.Replace(
                                    match.Groups[1].Value,
                                    string.Format("'<%=WebResource(\"{0}\")%>'", GetWebResourcePath(Path.GetDirectoryName(projectPath), ns, res.Path)));
                            }
                            break;
                        default:
                            regex = new Regex(string.Format(@"[(""|')](.*{0}.*)[(""|')]", Regex.Escape(Path.GetFileName(res.Path)), RegexOptions.IgnoreCase));
                            foreach (Match match in regex.Matches(fileContents))
                            {
                                fileContents = fileContents.Replace(
                                    match.Groups[1].Value,
                                    string.Format("<%=WebResource(\"{0}\")%>", GetWebResourcePath(Path.GetDirectoryName(projectPath), ns, res.Path)));
                            }
                            break;
                    }
                    Log("Done.");
                }
                sb.Append(new HtmlString(fileContents));
                sb.AppendFormat("\r\n]]></script></td></tr></table></div><br /><br />");
                //sb.AppendFormat("</pre></td></tr></table><br /><br />");

                idx++;
            }

            return sb.ToString();
        }

        private string[] GetAssemblyWebReferences(string rootPath, string ns, WebResourceCollection resourceList)
        {
            Log("   - Getting assembly web references...", true);
            List<string> references = new List<string>();

            foreach (WebResource resource in resourceList)
            {
                references.Add(new HtmlString(string.Format("[assembly: WebResource(\"{0}\", \"{1}\"{2})]", GetWebResourcePath(rootPath, ns, resource.Path), resource.MimeType, resource.PerformSubstitution ? ", PerformSubstitution = true" : "")).ToHtmlString());
            }
            Log("Done.");
            return references.ToArray();
        }

        private string[] GetAssemblyWebReferenceCalls(string rootPath, string ns, WebResourceCollection resourceList)
        {
            Log("   - Getting assembly web reference calls...", true);
            List<string> referenceCalls = new List<string>();
            string resPath = null;
            string resVar = null;
            foreach (WebResource resource in resourceList)
            {
                if (string.IsNullOrWhiteSpace(resource.Type))
                {
                    continue;
                }
                resPath = GetWebResourcePath(rootPath, ns, resource.Path);
                resVar = string.Format("URL_{0}", Path.GetFileNameWithoutExtension(resource.Path).Replace(" ", "").Replace(".", "_"));
                referenceCalls.Add(
                    new HtmlString(string.Format("//{0}\r\nstring {1} = Page.ClientScript.GetWebResourceUrl(typeof({2}), \"{3}\");\r\n",
                    Path.Combine(rootPath, resource.Path),
                    resVar,
                    resource.Type,
                    resPath)).ToHtmlString());
            }
            Log("Done.");
            return referenceCalls.ToArray();
        }

        private static string GetWebResourcePath(string rootPath, string rootNamespace, string fileName)
        {
            string relativePath = Common.Utils.GetRelativePath(rootPath, fileName);
            if (string.IsNullOrWhiteSpace(relativePath))
            {
                throw new Exception(string.Format("The file '{0}' must reside under root project path.", fileName));
            }
            List<string> parts = new List<string>();
            parts.Add(rootNamespace);
            parts.AddRange(Common.Utils.SplitPath(relativePath));
            return string.Join(".", parts);
        }

        private class WorkerArgs
        {
            public string projectPath;
            public string ns;
            public string[] substituteFileList;
            public WebResourceCollection resourceList;
            public string reportPath;

            public WorkerArgs()
            {

            }
        }
    }
}
